package com.xiaoyu.lib_av.proxy

import `in`.srain.cube.request.JsonData
import `in`.srain.cube.util.CLog
import com.xiaoyu.lib_av.datamodel.AVRoomPosition.ADMIN
import com.xiaoyu.lib_av.datamodel.AVRoomPosition.ANNOUNCER
import com.xiaoyu.lib_av.datamodel.AVRoomPosition.AUDIENCE
import com.xiaoyu.lib_av.datamodel.AVRoomPosition.AVRoomPositionDef
import com.xiaoyu.lib_av.datamodel.message.AVRoomMessage
import com.xiaoyu.lib_av.manager.AVRoomManager
import com.xiaoyu.lib_av.manager.AgoraManager
import `in`.srain.cube.concurrent.AppThreads
import io.agora.rtc.Constants.*
import io.agora.rtc.IRtcEngineEventHandler
import io.agora.rtc.RtcEngine
import io.agora.rtm.*

class AgoraAVRoomProxy : AgoraAbstractProxy(), IAVRoomProxy {

    companion object {
        const val SMOOTH_LEVEL = 3
        const val REPORT_VAD = true
        const val DISABLE_INTERVAL_MS = 0
        const val ENABLE_INTERVAL_MS = 500
    }

    private var mRtmChannel: RtmChannel? = null

    override val rtcEngineEventHandler: IRtcEngineEventHandler = object : IRtcEngineEventHandler() {

        override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
            AppThreads.runOnMainThread {
                CLog.i(TAG, "room onJoinChannelSuccess(), channel: %s, uid: %s, eplapsed: %s", channel, uid, elapsed)
                AVRoomManager.getInstance().onJoinChannelSuccess(uid)
            }
        }

        override fun onConnectionStateChanged(state: Int, reason: Int) {
            CLog.i(TAG, "room onConnectionStateChanged(), state=%s, reason=%s", state, reason)
            AVRoomManager.getInstance().onConnectionStateChanged(state, reason)
        }

        override fun onRejoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
            CLog.i(TAG, "room onRejoinChannelSuccess(), channel: %s, uid: %s, eplapsed: %s", channel, uid, elapsed)
        }

        override fun onLeaveChannel(stats: RtcStats?) {
            CLog.i(TAG, "room onLeaveChannel(), stats: %s", stats?.totalDuration)
        }

        override fun onUserJoined(uid: Int, elapsed: Int) {
            AppThreads.runOnMainThread {
                val fuid = uid.toString()
                CLog.i(TAG, "room onUserJoined(), uid: %s, eplapsed: %s", uid, elapsed)
                AVRoomManager.getInstance().onAnnouncerJoin(fuid)
            }
        }

        override fun onUserOffline(fuid: Int, reason: Int) {
            AppThreads.runOnMainThread {
                CLog.i(TAG, "room onUserOffline(), fuid: %s, reason: %s", fuid, reason)
                if (reason == USER_OFFLINE_QUIT || reason == USER_OFFLINE_BECOME_AUDIENCE) {
                    AVRoomManager.getInstance().onAnnouncerLeave(fuid.toString())
                }
            }
        }

        override fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) {
            AppThreads.runOnMainThread {
                val isTxQualityGood = txQuality == Quality.EXCELLENT || txQuality == Quality.GOOD
                val isRxQualityGood = rxQuality == Quality.EXCELLENT || rxQuality == Quality.GOOD
                val isQualityGood = isTxQualityGood && isRxQualityGood
                AVRoomManager.getInstance().onNetStatusReported(isQualityGood, uid.toString())
                CLog.d(TAG, "room onNetworkQuality(), uid: %s, isQualityGood: %s", uid, isQualityGood)
            }
        }

        override fun onAudioRouteChanged(routing: Int) {
            AppThreads.runOnMainThread {
                val headset = routing == AUDIO_ROUTE_HEADSET || routing == AUDIO_ROUTE_HEADSETBLUETOOTH || routing == AUDIO_ROUTE_HEADSETNOMIC
                AVRoomManager.getInstance().onAudioDeviceChanged(headset)
                CLog.i(TAG, "room onAudioRouteChanged(), isHeadset: %s", headset)
            }
        }

        override fun onClientRoleChanged(oldRole: Int, newRole: Int) {
            AppThreads.runOnMainThread {
                CLog.i(TAG, "room onClientRoleChanged(), oldRole: %s, newRole=%s", oldRole, newRole)
                AVRoomManager.getInstance().onPositionChange(newRole == CLIENT_ROLE_AUDIENCE)
            }
        }

        override fun onAudioVolumeIndication(speakers: Array<out AudioVolumeInfo>?, totalVolume: Int) {
            speakers ?: return
            for (speaker in speakers) {
                CLog.d(TAG, "room onAudioVolumeIndication() speaker uid=%s, volume=%s, vad=%s", speaker.uid, speaker.volume, speaker.vad)
            }
            var isSelf = false
            val audioVolumeIndicatorList = mutableListOf<AVRoomManager.Companion.AudioVolumeIndicator>()
            for (audioInfo in speakers) {
                isSelf = audioInfo.uid == 0
                val isSpeaking = audioInfo.volume > 0
                audioVolumeIndicatorList.add(AVRoomManager.Companion.AudioVolumeIndicator(audioInfo.uid.toString(), isSpeaking))
            }
            AVRoomManager.getInstance().onAudioVolumeChange(audioVolumeIndicatorList, isSelf)
        }

        override fun onError(err: Int) {
            CLog.e(TAG, "room onError(), code: %s", err)
            tryToastInDevMode("AVRoom rtc error: code=$err")
        }
    }

    private val rtmChannelListener = object : RtmChannelListener {
        override fun onAttributesUpdated(attributes: MutableList<RtmChannelAttribute>?) {
        }

        override fun onFileMessageReceived(p0: RtmFileMessage?, p1: RtmChannelMember?) {
        }

        override fun onImageMessageReceived(p0: RtmImageMessage?, p1: RtmChannelMember?) {
        }

        override fun onMemberCountUpdated(memberCount: Int) {
            AVRoomManager.getInstance().updateMemberCount(memberCount)
        }

        override fun onMessageReceived(rtmMessage: RtmMessage?, rtmChannelMember: RtmChannelMember?) {
            val message = AVRoomMessage.from(JsonData.create(rtmMessage?.text))
            CLog.i(TAG, "room onMessageReceived(), message=%s", message)
            AVRoomManager.getInstance().onMessageReceive(message)
        }

        override fun onMemberJoined(rtmChannelMember: RtmChannelMember?) {
        }

        override fun onMemberLeft(rtmChannelMember: RtmChannelMember?) {
        }
    }

    override fun onCreate() {
        super.onCreate(CHANNEL_PROFILE_LIVE_BROADCASTING)
        mRtcEngine?.setClientRole(CLIENT_ROLE_AUDIENCE)
        AgoraManager.getInstance().setAgoraProxy(this)
    }

    override fun onDestroy() {
        mRtmChannel?.release()
        RtcEngine.destroy()
        AgoraManager.getInstance().setAgoraProxy(null)
    }

    override fun joinRoom(channelId: String, callback: Runnable?) {
        mRtmChannel = rtmClient?.createChannel(channelId, rtmChannelListener)
        mRtmChannel?.join(object : ResultCallback<Void> {
            override fun onSuccess(v: Void?) {
                callingIdSet.add(channelId)
                AppThreads.runOnMainThread {
                    callback?.run()
                    CLog.i(TAG, "joinRoom() success")
                }
            }

            override fun onFailure(errorInfo: ErrorInfo?) {
                CLog.e(TAG, "joinRoom() failure, code=%s, desc=%s", errorInfo?.errorCode, errorInfo?.errorDescription)
                tryToastInDevMode("AVRoom rtm joinRoom() failure, code=${errorInfo?.errorCode}, desc=${errorInfo?.errorDescription}")
            }
        })
    }

    override fun leaveRoom(channelId: String?) {
        mRtcEngine?.enableAudioVolumeIndication(DISABLE_INTERVAL_MS, SMOOTH_LEVEL, REPORT_VAD)
        mRtcEngine?.leaveChannel()
        mRtmChannel?.leave(object : ResultCallback<Void?> {
            override fun onSuccess(v: Void?) {
                CLog.i(TAG, "leaveRoom() rtm leave success")
                callingIdSet.remove(channelId)
            }

            override fun onFailure(errorInfo: ErrorInfo?) {
                tryToastInDevMode("AVRoom rtm leaveRoom() failure, code=${errorInfo?.errorCode}, desc=${errorInfo?.errorDescription}")
                CLog.i(TAG, "leaveRoom() rtm leave failure, code=%s, desc=%s", errorInfo?.errorCode, errorInfo?.errorDescription)
            }
        })
    }

    override fun joinChannel(token: String?, channelId: String?, uid: String) {
        mRtcEngine?.joinChannel(token, channelId, null, uid.toInt())
        mRtcEngine?.enableAudioVolumeIndication(ENABLE_INTERVAL_MS, SMOOTH_LEVEL, REPORT_VAD)
    }

    override fun setPosition(@AVRoomPositionDef position: String) {
        CLog.i(TAG, "room setPosition() position=%s", position)
        when (position) {
            AUDIENCE -> mRtcEngine?.setClientRole(CLIENT_ROLE_AUDIENCE)
            ADMIN -> mRtcEngine?.setClientRole(CLIENT_ROLE_BROADCASTER)
            ANNOUNCER -> mRtcEngine?.setClientRole(CLIENT_ROLE_BROADCASTER)
        }
    }

    override fun isCalling(id: String?): Boolean {
        return AgoraManager.getInstance().isCalling(id)
    }

    override fun sendMessage(message: AVRoomMessage) {
        val rtmMessage = rtmClient?.createMessage()
        rtmMessage?.text = message.toJson().toString()

        val option = SendMessageOptions()
        mRtmChannel?.sendMessage(rtmMessage, option, object : ResultCallback<Void?> {
            override fun onSuccess(aVoid: Void?) {
                CLog.i(TAG, "room onMessageSend(), message=%s", message)
                AVRoomManager.getInstance().onMessageSend(message)
            }

            override fun onFailure(errorInfo: ErrorInfo?) {
                tryToastInDevMode("AVRoom rtm sendMessage() failure, code=${errorInfo?.errorCode}, desc=${errorInfo?.errorDescription}")
            }
        })
    }
}
